﻿#region using...
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Browser;
using Infusion.Silverlight.Controls.OpenOfficeXML;
#endregion

namespace Infusion.Silverlight.Controls.Input
{
    #region : Document :
    /// <summary>
    /// Rich text document.
    /// </summary>
    #region TemplateParts...
    [TemplatePart(Name = Document.GridWordDocument, Type = typeof(Grid))]
    [TemplatePart(Name = Document.GridHorizontalRuler, Type = typeof(Grid))]
    [TemplatePart(Name = Document.CanvasLeftMargin, Type = typeof(Canvas))]
    [TemplatePart(Name = Document.CanvasRightMargin, Type = typeof(Canvas))]
    [TemplatePart(Name = Document.RectHorizontalRulerBorder, Type = typeof(Rectangle))]
    [TemplatePart(Name = Document.CanvasHorizontalRulerMarks, Type = typeof(Canvas))]
    [TemplatePart(Name = Document.ClipHorizontalRulerMarks, Type = typeof(RectangleGeometry))]
    [TemplatePart(Name = Document.CanvasRulerControls, Type = typeof(Canvas))]
    [TemplatePart(Name = Document.CanvasFirstLineIndent, Type = typeof(Canvas))]
    [TemplatePart(Name = Document.PathFirstLineIndent, Type = typeof(Path))]
    [TemplatePart(Name = Document.CanvasHangingAndLeftIndent, Type = typeof(Canvas))]
    [TemplatePart(Name = Document.PathHangingIndent, Type = typeof(Path))]
    [TemplatePart(Name = Document.PathLeftIndent, Type = typeof(Path))]
    [TemplatePart(Name = Document.CanvasRightIndent, Type = typeof(Canvas))]
    [TemplatePart(Name = Document.PathRightIndent, Type = typeof(Path))]
    [TemplatePart(Name = Document.ScrollViewerLeftRulerAndPage, Type = typeof(ScrollViewer))]
    [TemplatePart(Name = Document.GridLeftRulerAndPage, Type = typeof(Grid))]
    [TemplatePart(Name = Document.GridVerticalRuler, Type = typeof(Grid))]
    [TemplatePart(Name = Document.CanvasTopMargin, Type = typeof(Canvas))]
    [TemplatePart(Name = Document.CanvasBottomMargin, Type = typeof(Canvas))]
    [TemplatePart(Name = Document.RectVerticalRulerBorder, Type = typeof(Rectangle))]
    [TemplatePart(Name = Document.CanvasVerticalRulerMarks, Type = typeof(Canvas))]
    [TemplatePart(Name = Document.ClipVerticalRulerMarks, Type = typeof(RectangleGeometry))]
    [TemplatePart(Name = Document.CanvasDocumentArea, Type = typeof(Canvas))]
    [TemplatePart(Name = Document.LineVerticalDashed, Type = typeof(Line))]
    [TemplatePart(Name = Document.LineHorizontalDashed, Type = typeof(Line))]
    [TemplatePart(Name = Document.GridPages, Type = typeof(Grid))]
    #endregion
    [ContentProperty("Pages")]
    public class Document : Control
    {
        #region TemplatePart Names...
        public const string GridWordDocument = "GridWordDocument";
        public const string GridHorizontalRuler = "GridHorizontalRuler";
        public const string CanvasLeftMargin = "CanvasLeftMargin";
        public const string CanvasRightMargin = "CanvasRightMargin";
        public const string RectHorizontalRulerBorder = "RectHorizontalRulerBorder";
        public const string CanvasHorizontalRulerMarks = "CanvasHorizontalRulerMarks";
        public const string ClipHorizontalRulerMarks = "ClipHorizontalRulerMarks";
        public const string CanvasRulerControls = "CanvasRulerControls";
        public const string CanvasFirstLineIndent = "CanvasFirstLineIndent";
        public const string PathFirstLineIndent = "PathFirstLineIndent";
        public const string CanvasHangingAndLeftIndent = "CanvasHangingAndLeftIndent";
        public const string PathHangingIndent = "PathHangingIndent";
        public const string PathLeftIndent = "PathLeftIndent";
        public const string CanvasRightIndent = "CanvasRightIndent";
        public const string PathRightIndent = "PathRightIndent";
        public const string ScrollViewerLeftRulerAndPage = "ScrollViewerLeftRulerAndPage";
        public const string GridLeftRulerAndPage = "GridLeftRulerAndPage";
        public const string GridVerticalRuler = "GridVerticalRuler";
        public const string CanvasTopMargin = "CanvasTopMargin";
        public const string CanvasBottomMargin = "CanvasBottomMargin";
        public const string RectVerticalRulerBorder = "RectVerticalRulerBorder";
        public const string CanvasVerticalRulerMarks = "CanvasVerticalRulerMarks";
        public const string ClipVerticalRulerMarks = "ClipVerticalRulerMarks";
        public const string CanvasDocumentArea = "CanvasDocumentArea";
        public const string LineVerticalDashed = "LineVerticalDashed";
        public const string LineHorizontalDashed = "LineHorizontalDashed";
        public const string GridPages = "GridPages";
        #endregion

        #region TemplatePart Variables...
        internal Grid gridWordDocument;
        internal Grid gridHorizontalRuler;
        internal Canvas canvasLeftMargin;
        internal Canvas canvasRightMargin;
        internal Rectangle rectHorizontalRulerBorder;
        internal Canvas canvasHorizontalRulerMarks;
        internal RectangleGeometry clipHorizontalRulerMarks;
        internal Canvas canvasRulerControls;
        internal Canvas canvasFirstLineIndent;
        internal Path pathFirstLineIndent;
        internal Canvas canvasHangingAndLeftIndent;
        internal Path pathHangingIndent;
        internal Path pathLeftIndent;
        internal Canvas canvasRightIndent;
        internal Path pathRightIndent;
        internal ScrollViewer scrollViewerLeftRulerAndPage;
        internal Grid gridLeftRulerAndPage;
        internal Grid gridVerticalRuler;
        internal Canvas canvasTopMargin;
        internal Canvas canvasBottomMargin;
        internal Rectangle rectVerticalRulerBorder;
        internal Canvas canvasVerticalRulerMarks;
        internal RectangleGeometry clipVerticalRulerMarks;
        internal Canvas canvasDocumentArea;
        internal Line lineVerticalDashed;
        internal Line lineHorizontalDashed;
        internal Grid gridPages;
        #endregion

        #region Dependency Properties...
        #region Title
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Input.Document.Title dependency property.
        /// </summary>
        public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(
            "Title",
            typeof(string),
            typeof(Document),
            null);
        #endregion

        #region Units
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Input.Document.Units dependency property.
        /// </summary>
        public static readonly DependencyProperty UnitsProperty = DependencyProperty.Register(
            "Units",
            typeof(DocumentUnit),
            typeof(Document),
            null);
        #endregion

        #region Pages
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Input.Document.Pages dependency property.
        /// </summary>
        public static readonly DependencyProperty PagesProperty = DependencyProperty.Register(
            "Pages",
            typeof(DocumentPageCollection),
            typeof(Document),
            null);
        #endregion

        #region ActivePageIndex
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Input.Document.ActivePageIndex dependency property.
        /// </summary>
        public static readonly DependencyProperty ActivePageIndexProperty = DependencyProperty.Register(
            "ActivePageIndex",
            typeof(int),
            typeof(Document),
            new PropertyMetadata(new PropertyChangedCallback(ActivePageIndexPropertyChanged)));
        #endregion

        #region Zoom
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Input.Document.Zoom dependency property.
        /// </summary>
        public static readonly DependencyProperty ZoomProperty = DependencyProperty.Register(
            "Zoom",
            typeof(double),
            typeof(Document),
            null);
        #endregion
        #endregion

        #region Dependency Property Changed Handlers...
        #region ActivePageIndexPropertyChanged
        private static void ActivePageIndexPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            Document document = (Document)obj;

            if (args.NewValue != null && document.templateApplied)
            {
                int newValue = Convert.ToInt32(args.NewValue);

                if (newValue > -1)
                {
                    //update horizontal ruler size and clip...
                    document.gridHorizontalRuler.Width = document.Pages[newValue].Size.PixelWidth - 2;
                    document.clipHorizontalRulerMarks.Rect = new Rect(0, 0, document.gridHorizontalRuler.Width, 20);

                    //update vertical ruler size, clip and positioning...
                    document.gridVerticalRuler.Margin = new Thickness(0, document.CalculatePageYOffset(newValue), 0, 0);
                    document.gridVerticalRuler.Height = document.Pages[newValue].Size.PixelHeight;
                    document.clipVerticalRulerMarks.Rect = new Rect(0, 0, 20, document.gridVerticalRuler.Height);

                    //update margins...
                    document.canvasLeftMargin.Width = OpenXMLHelper.ConvertPointsToPixels(document.Pages[newValue].LeftMargin);
                    document.canvasRightMargin.Width = OpenXMLHelper.ConvertPointsToPixels(document.Pages[newValue].RightMargin);
                    document.canvasTopMargin.Height = OpenXMLHelper.ConvertPointsToPixels(document.Pages[newValue].TopMargin);
                    document.canvasBottomMargin.Height = OpenXMLHelper.ConvertPointsToPixels(document.Pages[newValue].BottomMargin);

                    //update ruler markings...
                    document.UpdateRulerMarks();
                }
            }
        }
        #endregion
        #endregion

        #region Class Variables...
        private bool templateApplied = false;
        private bool mouseDownOnHorizontalRuler = false;
        private bool mouseDownOnVerticalRuler = false;
        ScrollBar verticalScrollBar = null;
        #endregion

        #region Constructor(s)...
        #region Document
        /// <summary>
        /// Rich text document.
        /// </summary>
        public Document()
            : base()
        {
            DefaultStyleKey = typeof(Document);

            HtmlPage.Window.AttachEvent("DOMMouseScroll", OnMouseWheel);
            HtmlPage.Window.AttachEvent("onmousewheel", OnMouseWheel);
            HtmlPage.Document.AttachEvent("onmousewheel", OnMouseWheel);

            ActivePageIndex = -1;
            Units = DocumentUnit.Inches;
            Pages = new DocumentPageCollection();
        }
        #endregion
        #endregion

        #region Event Handlers...
        #region OnMouseWheel
        private void OnMouseWheel(object sender, HtmlEventArgs args)
        {
            Point clickPoint = new Point(args.OffsetX, args.OffsetY);
            
            //List<UIElement> itemsHit = (List<UIElement>)HitTest(clickPoint);
            List<UIElement> itemsHit = (List<UIElement>)VisualTreeHelper.FindElementsInHostCoordinates(clickPoint, this);

            if (itemsHit.Contains(this))
            {
                double delta = 0;
                ScriptObject e = args.EventObject;

                if (e.GetProperty("wheelDelta") != null) // IE and Opera
                {
                    delta = ((double)e.GetProperty("wheelDelta"));

                    if (HtmlPage.Window.GetProperty("opera") != null)
                        delta = -delta;
                }
                else if (e.GetProperty("detail") != null) // Mozilla and Safari
                {
                    delta = -((double)e.GetProperty("detail"));
                }

                //delta = Math.Sign(delta);

                if (delta != 0)
                {
                    args.PreventDefault();
                    e.SetProperty("returnValue", false);
                }

                scrollViewerLeftRulerAndPage.ScrollToVerticalOffset(scrollViewerLeftRulerAndPage.VerticalOffset - (delta / 4d));
            }
        }
        #endregion

        #region gridWordDocument_Loaded
        private void gridWordDocument_Loaded(object sender, RoutedEventArgs e)
        {
            verticalScrollBar = Helper.GetChildByName(scrollViewerLeftRulerAndPage, "VerticalScrollBar") as ScrollBar;
            scrollViewerLeftRulerAndPage.SizeChanged += new SizeChangedEventHandler(scrollViewerLeftRulerAndPage_SizeChanged);

            scrollViewerLeftRulerAndPage_SizeChanged(this, null);
        }
        #endregion

        #region scrollViewerLeftRulerAndPage_SizeChanged
        private void scrollViewerLeftRulerAndPage_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            verticalScrollBar.Height = scrollViewerLeftRulerAndPage.ActualHeight - 50;
            
            gridPages.Width = scrollViewerLeftRulerAndPage.ActualWidth -
                gridLeftRulerAndPage.ColumnDefinitions[0].ActualWidth -
                verticalScrollBar.ActualWidth;

            canvasDocumentArea.Height = gridPages.ActualHeight + 13;
        }
        #endregion

        #region canvasHorizontalRulerMarks_MouseMove
        private void canvasHorizontalRulerMarks_MouseMove(object sender, MouseEventArgs e)
        {
            Point pointInRuler = e.GetPosition(canvasHorizontalRulerMarks);

            if (!mouseDownOnHorizontalRuler)
            {
                if ((pointInRuler.X > canvasLeftMargin.ActualWidth - 2 &&
                     pointInRuler.X < canvasLeftMargin.ActualWidth + 2) ||
                    (pointInRuler.X > Pages[ActivePageIndex].Size.PixelWidth - canvasRightMargin.Width - 2 &&
                     pointInRuler.X < Pages[ActivePageIndex].Size.PixelWidth - canvasRightMargin.Width + 2))
                {
                    canvasHorizontalRulerMarks.Cursor = Cursors.SizeWE;
                }
                else
                {
                    canvasHorizontalRulerMarks.Cursor = Cursors.Arrow;
                }
            }
        }
        #endregion

        #region canvasHorizontalRulerMarks_MouseLeftButtonDown
        private void canvasHorizontalRulerMarks_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (canvasHorizontalRulerMarks.Cursor == Cursors.SizeWE)
            {
                mouseDownOnHorizontalRuler = true;
                Point pointInRuler = e.GetPosition(null);

                lineVerticalDashed.X1 =
                    lineVerticalDashed.X2 = pointInRuler.X - 26;

                lineVerticalDashed.Y1 = 0;
                lineVerticalDashed.Y2 = gridPages.ActualHeight + 13;

                lineVerticalDashed.Visibility = Visibility.Visible;
            }
        }
        #endregion

        #region canvasHorizontalRulerMarks_MouseLeftButtonUp
        private void canvasHorizontalRulerMarks_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            mouseDownOnHorizontalRuler = false;
            Point pointInRuler = e.GetPosition(canvasHorizontalRulerMarks);

            lineVerticalDashed.Visibility = Visibility.Collapsed;
        }
        #endregion

        #region canvasVerticalRulerMarks_MouseMove
        private void canvasVerticalRulerMarks_MouseMove(object sender, MouseEventArgs e)
        {
            Point pointInRuler = e.GetPosition(canvasVerticalRulerMarks);

            if (!mouseDownOnVerticalRuler)
            {
                if ((pointInRuler.Y > canvasTopMargin.ActualHeight - 2 &&
                     pointInRuler.Y < canvasTopMargin.ActualHeight + 2) ||
                    (pointInRuler.Y > Pages[ActivePageIndex].Size.PixelHeight - canvasBottomMargin.Height - 2 &&
                     pointInRuler.Y < Pages[ActivePageIndex].Size.PixelHeight - canvasBottomMargin.Height + 2))
                {
                    canvasVerticalRulerMarks.Cursor = Cursors.SizeNS;
                }
                else
                {
                    canvasVerticalRulerMarks.Cursor = Cursors.Arrow;
                }
            }
        }
        #endregion

        #region canvasVerticalRulerMarks_MouseLeftButtonDown
        private void canvasVerticalRulerMarks_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (canvasVerticalRulerMarks.Cursor == Cursors.SizeNS)
            {
                mouseDownOnVerticalRuler = true;
                Point pointInRuler = e.GetPosition(canvasVerticalRulerMarks);

                lineHorizontalDashed.Y1 =
                    lineHorizontalDashed.Y2 = pointInRuler.Y + 13;

                lineHorizontalDashed.X1 = 0;
                lineHorizontalDashed.X2 = gridPages.ActualWidth;

                lineHorizontalDashed.Visibility = Visibility.Visible;
            }
        }
        #endregion

        #region canvasVerticalRulerMarks_MouseLeftButtonUp
        private void canvasVerticalRulerMarks_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            mouseDownOnVerticalRuler = false;
            Point pointInRuler = e.GetPosition(canvasVerticalRulerMarks);

            lineHorizontalDashed.Visibility = Visibility.Collapsed;
        }
        #endregion
        #endregion

        #region Methods...
        #region CalculatePageYOffset
        private double CalculatePageYOffset(int pageIndex)
        {
            double offset = 13d; //top margin

            for (int i = 0; i < pageIndex; i++)
            {
                offset += Pages[i].Size.PixelHeight + 13d; //height + bottom margin;
            }

            return offset;
        }
        #endregion

        #region UpdateRulerMarks
        private void UpdateRulerMarks()
        {
            SolidColorBrush blackBrush = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));
            RotateTransform rotateTransformMinus90 = new RotateTransform();
            rotateTransformMinus90.Angle = -90;

            double widthInUnits = Pages[ActivePageIndex].Size.GetWidth(Units);
            double heightInUnits = Pages[ActivePageIndex].Size.GetHeight(Units);

            double pixelsPerUnit = Pages[ActivePageIndex].Size.PixelWidth / widthInUnits;

            double unitDivisions = Convert.ToDouble(Units);

            //horizontal ruler...
            canvasHorizontalRulerMarks.Children.Clear();

            int unitsToTheRight = (int)Math.Round(widthInUnits);
            int unitsToTheLeft = -1 * unitsToTheRight;

            for (int i = unitsToTheLeft * Convert.ToInt32(Units); i <= unitsToTheRight * Convert.ToInt32(Units); i++)
            {
                if (i != 0)
                {
                    double mod = i % unitDivisions;
                    double halfMod = i % (unitDivisions / 2d);

                    if (mod == 0) //numbers...
                    {
                        TextBlock text = new TextBlock();
                        text.FontFamily = new FontFamily("Calibri");
                        text.FontSize = 10.5d;
                        text.Text = Math.Abs((double)i / unitDivisions).ToString();
                        text.Margin = new Thickness(canvasLeftMargin.Width + ((double)i / unitDivisions * pixelsPerUnit) - (text.Text.Length * 3d), -1d, 0, 0);

                        canvasHorizontalRulerMarks.Children.Add(text);
                    }
                    else //notches (large notch if halfMod == 0)...
                    {
                        Line line = new Line();
                        line.X1 = canvasLeftMargin.Width + ((double)i / unitDivisions * pixelsPerUnit);
                        line.Y1 = (halfMod == 0 ? 3.5 : 4.5);
                        line.X2 = line.X1;
                        line.Y2 = (halfMod == 0 ? 9.5 : 7.5);
                        line.Stroke = blackBrush;
                        line.StrokeThickness = 0.5;

                        canvasHorizontalRulerMarks.Children.Add(line);
                    }

                    //notches under the ruler...  <<TAB STOPS!!>>
                    if (mod == 0 || halfMod == 0)
                    {
                        Line line = new Line();
                        line.X1 = canvasLeftMargin.Width + ((double)i / unitDivisions * pixelsPerUnit);
                        line.Y1 = 14;
                        line.X2 = line.X1;
                        line.Y2 = 16.5;
                        line.Stroke = blackBrush;
                        line.StrokeThickness = 0.5;

                        if (line.X1 > canvasLeftMargin.Width && line.X1 < Pages[ActivePageIndex].Size.PixelWidth - canvasRightMargin.Width)
                            canvasHorizontalRulerMarks.Children.Add(line);
                    }
                }
            }

            //vertical ruler...
            canvasVerticalRulerMarks.Children.Clear();

            unitsToTheRight = (int)Math.Round(heightInUnits);
            unitsToTheLeft = -1 * unitsToTheRight;

            for (int i = unitsToTheLeft * Convert.ToInt32(Units); i <= unitsToTheRight * Convert.ToInt32(Units); i++)
            {
                if (i != 0)
                {
                    double mod = i % unitDivisions;
                    double halfMod = i % (unitDivisions / 2d);

                    if (mod == 0) //numbers...
                    {
                        TextBlock text = new TextBlock();
                        text.FontFamily = new FontFamily("Calibri");
                        text.FontSize = 10.5d;
                        text.RenderTransform = rotateTransformMinus90;
                        text.Text = Math.Abs((double)i / unitDivisions).ToString();
                        text.Margin = new Thickness(-1d, canvasTopMargin.Height + ((double)i / unitDivisions * pixelsPerUnit) + (text.Text.Length * 4d), 0, 0);

                        canvasVerticalRulerMarks.Children.Add(text);
                    }
                    else //notches...
                    {
                        Line line = new Line();
                        line.X1 = (halfMod == 0 ? 3 : 5);
                        line.Y1 = canvasTopMargin.Height + ((double)i / unitDivisions * pixelsPerUnit) + 0.5d;
                        line.X2 = (halfMod == 0 ? 10 : 8);
                        line.Y2 = line.Y1;
                        line.Stroke = blackBrush;
                        line.StrokeThickness = 0.5;

                        canvasVerticalRulerMarks.Children.Add(line);
                    }
                }
            }
        }
        #endregion

        #region OnApplyTemplate
        /// <summary>
        /// Invoked whenever application code or internal processes (such as a rebuilding layout pass) call System.Windows.Controls.Control.ApplyTemplate().
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (!templateApplied)
            {
                gridWordDocument = (Grid)GetTemplateChild(GridWordDocument);
                gridHorizontalRuler = (Grid)GetTemplateChild(GridHorizontalRuler);
                canvasLeftMargin = (Canvas)GetTemplateChild(CanvasLeftMargin);
                canvasRightMargin = (Canvas)GetTemplateChild(CanvasRightMargin);
                rectHorizontalRulerBorder = (Rectangle)GetTemplateChild(RectHorizontalRulerBorder);
                canvasHorizontalRulerMarks = (Canvas)GetTemplateChild(CanvasHorizontalRulerMarks);
                clipHorizontalRulerMarks = (RectangleGeometry)GetTemplateChild(ClipHorizontalRulerMarks);
                canvasRulerControls = (Canvas)GetTemplateChild(CanvasRulerControls);
                canvasFirstLineIndent = (Canvas)GetTemplateChild(CanvasFirstLineIndent);
                pathFirstLineIndent = (Path)GetTemplateChild(PathFirstLineIndent);
                canvasHangingAndLeftIndent = (Canvas)GetTemplateChild(CanvasHangingAndLeftIndent);
                pathHangingIndent = (Path)GetTemplateChild(PathHangingIndent);
                pathLeftIndent = (Path)GetTemplateChild(PathLeftIndent);
                canvasRightIndent = (Canvas)GetTemplateChild(CanvasRightIndent);
                pathRightIndent = (Path)GetTemplateChild(PathRightIndent);
                scrollViewerLeftRulerAndPage = (ScrollViewer)GetTemplateChild(ScrollViewerLeftRulerAndPage);
                gridLeftRulerAndPage = (Grid)GetTemplateChild(GridLeftRulerAndPage);
                gridVerticalRuler = (Grid)GetTemplateChild(GridVerticalRuler);
                canvasTopMargin = (Canvas)GetTemplateChild(CanvasTopMargin);
                canvasBottomMargin = (Canvas)GetTemplateChild(CanvasBottomMargin);
                rectVerticalRulerBorder = (Rectangle)GetTemplateChild(RectVerticalRulerBorder);
                canvasVerticalRulerMarks = (Canvas)GetTemplateChild(CanvasVerticalRulerMarks);
                clipVerticalRulerMarks = (RectangleGeometry)GetTemplateChild(ClipVerticalRulerMarks);
                canvasDocumentArea = (Canvas)GetTemplateChild(CanvasDocumentArea);
                lineVerticalDashed = (Line)GetTemplateChild(LineVerticalDashed);
                lineHorizontalDashed = (Line)GetTemplateChild(LineHorizontalDashed);
                gridPages = (Grid)GetTemplateChild(GridPages);

                gridWordDocument.Loaded += new RoutedEventHandler(gridWordDocument_Loaded);

                canvasHorizontalRulerMarks.MouseMove += new MouseEventHandler(canvasHorizontalRulerMarks_MouseMove);
                canvasHorizontalRulerMarks.MouseLeftButtonDown += new MouseButtonEventHandler(canvasHorizontalRulerMarks_MouseLeftButtonDown);
                canvasHorizontalRulerMarks.MouseLeftButtonUp += new MouseButtonEventHandler(canvasHorizontalRulerMarks_MouseLeftButtonUp);

                canvasVerticalRulerMarks.MouseMove += new MouseEventHandler(canvasVerticalRulerMarks_MouseMove);
                canvasVerticalRulerMarks.MouseLeftButtonDown += new MouseButtonEventHandler(canvasVerticalRulerMarks_MouseLeftButtonDown);
                canvasVerticalRulerMarks.MouseLeftButtonUp += new MouseButtonEventHandler(canvasVerticalRulerMarks_MouseLeftButtonUp);

                for (int pageIndex = 0; pageIndex < Pages.Count; pageIndex++)
                {
                    if (gridPages.RowDefinitions.Count - 1 < pageIndex)
                        gridPages.RowDefinitions.Add(Helper.CreateGridRowDefinition(Pages[pageIndex].Size.PixelHeight + 13));

                    Pages[pageIndex].VerticalAlignment = VerticalAlignment.Top;
                    Pages[pageIndex].SetValue(Grid.RowProperty, pageIndex);
                    Pages[pageIndex].Document = this;

                    gridPages.Children.Add(Pages[pageIndex]);
                }

                templateApplied = true;
                ActivePageIndex = 0;
            }
        }
        #endregion
        #endregion

        #region Properties...
        #region Title
        /// <summary>
        /// Gets or sets the title of a <see cref="Infusion.Silverlight.Controls.Input.Document.Title"/>.
        /// </summary>
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }
        #endregion

        #region Units
        /// <summary>
        /// Gets or sets the units used in this <see cref="Infusion.Silverlight.Controls.Input.Document"/>.
        /// </summary>
        public DocumentUnit Units
        {
            get { return (DocumentUnit)GetValue(UnitsProperty); }
            set { SetValue(UnitsProperty, value); }
        }
        #endregion

        #region Pages
        /// <summary>
        /// Gets or sets the collection of <see cref="Infusion.Silverlight.Controls.Input.DocumentPage"/> in this <see cref="Infusion.Silverlight.Controls.Input.Document"/>.
        /// </summary>
        public DocumentPageCollection Pages
        {
            get { return (DocumentPageCollection)GetValue(PagesProperty); }
            set { SetValue(PagesProperty, value); }
        }
        #endregion

        #region ActivePageIndex
        /// <summary>
        /// Gets or sets the index of the active page in the <see cref="Infusion.Silverlight.Controls.Input.DocumentPageCollection"/> of this <see cref="Infusion.Silverlight.Controls.Input.Document"/>.
        /// </summary>
        public int ActivePageIndex
        {
            get { return (int)GetValue(ActivePageIndexProperty); }
            set { SetValue(ActivePageIndexProperty, value); }
        }
        #endregion

        #region Zoom
        /// <summary>
        /// Gets or sets the current zoom of this <see cref="Infusion.Silverlight.Controls.Input.Document"/>.
        /// </summary>
        public double Zoom
        {
            get { return (double)GetValue(ZoomProperty); }
            set { SetValue(ZoomProperty, value); }
        }
        #endregion
        #endregion
    }
    #endregion

    #region : DocumentPageCollection :
    /// <summary>
    /// A <see cref="System.Collections.ObjectModel.ObservableCollection&lt;T&gt;"/> of <see cref="Infusion.Silverlight.Controls.Input.DocumentPage"/>s
    /// </summary>
    public class DocumentPageCollection : ObservableCollection<DocumentPage>
    {
    }
    #endregion
}